using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;

namespace MicroserviceFramework.Analyzers;

[Generator(LanguageNames.CSharp)]
public class RepositoryInterfaceGenerator : IIncrementalGenerator
{
    private static readonly string AggregateRootInterface
        = "MicroserviceFramework.Domain.IAggregateRoot<";

    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var pipeline = context.SyntaxProvider.CreateSyntaxProvider(
            static (syntaxNode, _) => syntaxNode.IsKind(SyntaxKind.ClassDeclaration),
            static (GeneratorSyntaxContext context, CancellationToken cancellationToken) =>
            {
                var classDeclarationSyntax = (ClassDeclarationSyntax)context.Node;
                var assemblyClassTypeSymbol =
                    context.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax, cancellationToken);

                if (assemblyClassTypeSymbol == null)
                {
                    return null;
                }

                foreach (var @interface in assemblyClassTypeSymbol.AllInterfaces)
                {
                    var interfaceName = @interface.ToDisplayString();
                    if (!interfaceName.StartsWith(AggregateRootInterface))
                    {
                        continue;
                    }

                    var key = interfaceName.Substring(
                        AggregateRootInterface.Length,
                        interfaceName.Length - AggregateRootInterface.Length - 1);
                    return new Model(
                        assemblyClassTypeSymbol.ContainingNamespace?.ToDisplayString(
                            SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(
                                SymbolDisplayGlobalNamespaceStyle.Omitted)),
                        assemblyClassTypeSymbol.Name, key);
                }

                return null;
            });

        context.RegisterSourceOutput(pipeline, static (context, model) =>
        {
            if (model == null)
            {
                return;
            }

            var repoNamespace = model.Namespace.Replace("AggregateRoots", "Repositories");
            var sourceText = SourceText.From($$"""
                                               // <auto-generated time='{{DateTimeOffset.Now:yyyy-MM-dd HH:mm:ss}}' />
                                               using MicroserviceFramework.Domain;
                                               using MicroserviceFramework.Extensions.DependencyInjection;
                                               using E = {{model.Namespace}}.{{model.ClassName}};

                                               namespace {{repoNamespace}};

                                               public partial interface I{{model.ClassName}}Repository
                                                   : IRepository<E, {{model.Key}}>, IScopeDependency
                                               {
                                               }
                                               """, Encoding.UTF8);

            context.AddSource($"{repoNamespace}.{model.ClassName}.g.cs", sourceText);
        });
    }

    private class Model(string @namespace, string className, string key)
    {
        public string Namespace { get; set; } = @namespace;
        public string ClassName { get; set; } = className;

        public string Key { get; set; } = key;
    }
}
